package com.hsgene.common.util.resolver;

import com.google.common.base.Strings;
import com.hsgene.common.util.feign.FeignRequestInterceptor;
import com.hsgene.common.util.redis.RedisConstants;
import com.hsgene.common.util.validation.FieldChecker;
import com.hsgene.common.util.web.access.exception.*;
import com.hsgene.common.util.web.session.SessionConstants;
import com.hsgene.common.util.web.session.UserInfo;
import com.hsgene.common.util.web.session.UserInfoExtra;
import com.hsgene.common.util.web.session.UserSession;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

/**
 * 处理IgnoreUserToken注解
 *
 * @author wangbing
 * @version 1.0, 2018/11/9
 */
public class IgnoreUserTokenHandlerInterceptor extends HandlerInterceptorAdapter {

    @Resource
    private RedisTemplate<String, UserSession> redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String feignClient = request.getHeader(FeignRequestInterceptor.FEIGN_CLIENT);
        if (!StringUtils.isEmpty(feignClient) && feignClient.equals(FeignRequestInterceptor.FEIGN_CLIENT)) {
            return super.preHandle(request, response, handler);
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;
        IgnoreUserToken annotation = handlerMethod.getBeanType().getAnnotation(IgnoreUserToken.class);
        if (annotation == null) {
            annotation = handlerMethod.getMethodAnnotation(IgnoreUserToken.class);
        }
        if (annotation != null) {
            return super.preHandle(request, response, handler);
        }
        String headerPlatform = null;
        Integer platform;
        try {
            headerPlatform = request.getParameter("platform");
            FieldChecker.checkEmpty(headerPlatform, "客户端标识platform");
            platform = Integer.valueOf(headerPlatform);
        } catch (NumberFormatException numberE) {
            throw new IllegalArgumentException("非法的客户端标识：" + headerPlatform);
        }
        if (platform == 2 || platform == 3) {
            // 花生仁和金琉璃不校验用户是否登录
            return super.preHandle(request, response, handler);
        }
        validate(request);
        return super.preHandle(request, response, handler);
    }

    /**
     * 此处方法所有异常抛出，均在{@link com.hsgene.common.util.exception.GlobalExceptionHandler}中进行处理
     *
     * @param request HttpServletRequest
     */
    public void validate(HttpServletRequest request) {
        String sessionId = request.getHeader(SessionConstants.SESSION_ID_KEY);
        String accessToken = request.getHeader(SessionConstants.ACCESS_TOKEN_KEY);
        if (!Strings.isNullOrEmpty(sessionId) && !Strings.isNullOrEmpty(accessToken)) {
            UserSession session = redisTemplate.opsForValue().get(getRedisSessionKey(sessionId));
            if (session != null) {
                UserInfo userInfo = session.getUserInfo();
                if ("0".equals(userInfo.getUserType())) {
                    UserInfoExtra extra = userInfo.getExtra();
                    if (extra.getFirstLogin()) {
                        throw new FirstLoginException();
                    }
                }
                if (accessToken.equals(session.getAccessToken())) {
                    if (!session.isAccessTokenExpired()) {
                        // fix[BUG-17741]，刷新用户的access_token过期时间
                        if (UserSession.ACCESS_TOKEN_EXPIRE_TIME > 0) {
                            LocalDateTime expireTime = LocalDateTime.now().plus(UserSession.ACCESS_TOKEN_EXPIRE_TIME, ChronoUnit.MINUTES);
                            session.setExpireTime(expireTime);
                            // 将会话信息存入Redis
                            redisTemplate.opsForValue().set(getRedisSessionKey(sessionId), session);
                            return;
                        }
                    } else {
                        throw new TokenExpiredException();
                    }
                } else if (accessToken.equals(session.getOldAccessToken())) {
                    throw new TokenRefreshedException();
                } else {
                    throw new TokenLoggedOtherException();
                }
            }
        }

        throw new TokenNoneException();
    }

    /**
     * 获取Redis缓存Session键
     *
     * @param sessionId 用户sessionId
     * @return Redis缓存Session键
     */
    public static String getRedisSessionKey(String sessionId) {
        return RedisConstants.DEFAULT_REDIS_PREFIX + RedisConstants.COLON_SPLIT + SessionConstants.REDIS_SESSION_KEY_PREFIX + sessionId;
    }
}
